/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/8/14
 *
 * History:
 *
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#ifndef MELIS_OS
#include <netinet/in.h>
#endif
#endif

#include "osal_eventfd.h"
#include "osal_log.h"

typedef struct
{
    int                 sock_send;
    int                 sock_recv;
    struct sockaddr_in  addr_send;
    struct sockaddr_in  addr_recv;
} eventfd_ctx_t;

_handle_t ld_eventfd_create(unsigned int initval, int flags)
{
    socklen_t addr_len = sizeof (struct sockaddr_in);
    uint16_t port = 0;
    int ret = 0;

    eventfd_ctx_t * p_ctx = malloc(sizeof(eventfd_ctx_t));
    if (p_ctx == NULL)
    {
        return (_handle_t)NULL;
    }

    memset(p_ctx, 0, sizeof(eventfd_ctx_t));

    // create recv socket
    p_ctx->sock_recv = socket(PF_INET, SOCK_DGRAM, 0);
    if (p_ctx->sock_recv == -1) {
        goto _failed_;
    }
    p_ctx->addr_recv.sin_family = AF_INET;
    p_ctx->addr_recv.sin_port = htons(0);
    p_ctx->addr_recv.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(p_ctx->sock_recv, (struct sockaddr *)&p_ctx->addr_recv, addr_len);
    if (ret == -1) {
        loge("bind failed");
        goto _failed_;
    }

    ret = getsockname(p_ctx->sock_recv, (struct sockaddr *)&p_ctx->addr_recv, &addr_len);
    if (ret == 0) {
        port = ntohs(p_ctx->addr_recv.sin_port);
        logv("sockfd addr: %s:%d", inet_ntoa(p_ctx->addr_recv.sin_addr), port);
    } else {
        loge("getsockname failed");
        goto _failed_;
    }

    // create send socket
    p_ctx->sock_send = socket(PF_INET, SOCK_DGRAM, 0);
    if (p_ctx->sock_send == -1) {
        goto _failed_;
    }
    p_ctx->addr_send.sin_family = AF_INET;
    p_ctx->addr_send.sin_port = htons(port);
    p_ctx->addr_send.sin_addr.s_addr = htonl(INADDR_ANY);

    return (_handle_t)p_ctx;

_failed_:
    if (p_ctx->sock_send > 0) {
        close(p_ctx->sock_send);
    }
    if (p_ctx) {
        free(p_ctx);
    }
    return (_handle_t)NULL;
}

void ld_eventfd_destroy(_handle_t h_fd)
{
    eventfd_ctx_t * p_ctx = (eventfd_ctx_t *)h_fd;
    if (p_ctx != NULL)
    {
        if (p_ctx->sock_send > 0) {
            close(p_ctx->sock_send);
            p_ctx->sock_send = -1;
        }
        if (p_ctx->sock_recv > 0) {
            close(p_ctx->sock_recv);
            p_ctx->sock_recv = -1;
        }
        free(p_ctx);
        p_ctx = NULL;
    }
}

int ld_eventfd_getfd(_handle_t h_fd)
{
    eventfd_ctx_t * p_ctx = (eventfd_ctx_t *)h_fd;
    if (p_ctx == NULL) {
        return -1;
    }
    return p_ctx->sock_recv;
}

int ld_eventfd_read(_handle_t h_fd, int * cnt)
{
    eventfd_ctx_t * p_ctx = (eventfd_ctx_t *)h_fd;
    socklen_t addr_len = sizeof(struct sockaddr_in);
    int ret = 0;
    if (p_ctx == NULL) {
        return -1;
    }

    ret = recvfrom(p_ctx->sock_recv, cnt, sizeof(int), 0,
                (struct sockaddr*)&p_ctx->addr_recv, &addr_len);
    return ret;
}

int ld_eventfd_write(_handle_t h_fd, int cnt)
{
    eventfd_ctx_t * p_ctx = (eventfd_ctx_t *)h_fd;
    int ret = 0;
    if (p_ctx == NULL) {
        return -1;
    }

    ret = sendto(p_ctx->sock_send, (char *)&cnt, sizeof(cnt), 0,
                (struct sockaddr*)&p_ctx->addr_send, sizeof(struct sockaddr));
    return ret;
}

